﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using AGDN;
using System;

namespace Sudum
{
	class GameLevel
	{
		public GameLevel()
		{
			m_currentLevel = this;
			cam = null;
			textRenderer = new SpriteBatch(Sudum.Me.GraphicsDevice);
            playerShip = null;
            bulletManager = null;
            enemyManager = null;
            collisionManager = null;
		}

        public void Update()
        {
            playerShip.Update();
            enemyManager.Update();
            bulletManager.Update();
            stars.Update();
            collisionManager.Update();
        }

		void DrawDebugText()
		{
			//Vector2 vPos = new Vector2(10f, 30f);
			//string text = "Player Phase: " + poly.Length.ToString();

			//textRenderer.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.SaveState);
			//textRenderer.DrawString(Sudum.Me.DebugTextFont, text, vPos, Color.Red);
			//textRenderer.End();
		}

        public float GetTubeLength()
        {
            return tubeLength;
        }

		public void Draw()
		{
            DrawTube();
            playerShip.Draw();
            stars.Draw();

#if DEBUG
			DrawLines();
			DrawDebugText();
#endif
            // if we don't draw the bullets as the last thing, the stars/debugging lines will be affected
            // by a change in the device state (not sure exactly about this)
            bulletManager.Draw();

            enemyManager.Draw();
		}

		public Model GetTubeModel()
		{
			return tubeModel;
		}

		public bool Load()
		{
			// load model here
			tubeModel = Sudum.Me.Content.Load<Model>("Models\\tube");
			
			SetLevelData();

			// create the camera at origin and force it to look at our tube            
			CreateCamera(camPos, tubePos);

			LoadGridShader();

            InitPolyLine();

            // load vehicle model
            playerShip = new PlayerShip();
            playerShip.Init(this);
            collisionManager = new CollisionManager();
            bulletManager = new BulletManager(this);
            enemyManager = new EnemyManager(this);
            stars = new StarsEffect(GetLevelCamera());

			return true;
		}

        void InitPolyLine()
        {
            poly = new Polyline(GetTubeModelMesh());

#if DEBUG
            // create debugging lines
            pointList = new VertexPositionColor[poly.VerticesCount * 2];

            polyVerticesCount = poly.VerticesCount;

            // uncomment the code in the next two loops to see that the tube is culling the debugging lines
            for (int i = 0, j = 1; i < poly.VerticesCount; i += 2, j += 2)
            {
                //Vector3 dir = poly.GetVertexPosition(i) - tubePos;
                //dir.Normalize();
                Vector3 p1 = poly.GetVertexPosition(i);// +dir * 100;
                Vector3 p2 = tubePos;
                //p1.Y += 50;
                //p2.Y += 50;
                pointList[i] = new VertexPositionColor(p1, Color.White);
                pointList[j] = new VertexPositionColor(p2, Color.White);
            }
            for (int i = 1, j = 0; i < poly.VerticesCount; i += 2, j += 2)
            {
                //Vector3 dir = poly.GetVertexPosition(i) - tubePos;
                //dir.Normalize();
                Vector3 p1 = poly.GetVertexPosition(i);// +dir * 100;
                Vector3 p2 = tubePos;
                //p1.Y += 50;
                //p2.Y += 50;
                pointList[i + poly.VerticesCount] = new VertexPositionColor(p1, Color.White);
                pointList[j + poly.VerticesCount] = new VertexPositionColor(p2, Color.White);
            }
#endif
        }

        public Vector3 GetPositionFromPolyline(float polylinePhase, ref Vector3 normal)
        {
            
            return poly.Evaluate(polylinePhase, ref normal);
        }

        public Camera GetLevelCamera()
        {
            return cam;
        }

        public float GetTubeRotationX()
        {
            return tubeRotX;
        }

		void LoadGridShader()
		{
			gridShaderEffect = Sudum.Me.Content.Load<Effect>("Shaders\\Grid");

			// assign our shader to the tube model
            foreach (ModelMesh mesh in tubeModel.Meshes)
            {
                foreach (ModelMeshPart meshPart in mesh.MeshParts)
                    meshPart.Effect = gridShaderEffect;
            }
		}

		public void CreateCamera(Vector3 camPos, Vector3 camTarget)
		{
			if (cam == null)
			{
				cam = new Camera(camPos, camTarget);
			}
		}

		public static GameLevel Current()
		{
			return m_currentLevel;
		}

#if DEBUG
        void DrawLines()
        {
            basicEffect.VertexColorEnabled = true;

            basicEffect.World = Matrix.CreateRotationX(tubeRotX) *
                            Matrix.CreateTranslation(tubePos);
            basicEffect.Projection = cam.GetProjection();
            basicEffect.View = cam.GetView();

            basicEffect.Begin();
            basicEffect.CurrentTechnique.Passes[0].Begin();

            Sudum.Me.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>
                (PrimitiveType.LineList, pointList, 0, poly.VerticesCount);

            basicEffect.CurrentTechnique.Passes[0].End();
            basicEffect.End();
        }
#endif

		public void DrawTube()
		{
            

			// Copy any parent transforms.
			Matrix[] transforms = new Matrix[tubeModel.Bones.Count];
			tubeModel.CopyAbsoluteBoneTransformsTo(transforms);

			// Draw the model. A model can have multiple meshes, so loop.
			foreach (ModelMesh mesh in tubeModel.Meshes)
			{
				// apply our shader before drawing
				foreach (Effect effect in mesh.Effects)
				{
					effect.CurrentTechnique = gridShaderEffect.Techniques["GridShaded"];

					Matrix world = transforms[mesh.ParentBone.Index] *
						Matrix.CreateRotationX(tubeRotX) *
						Matrix.CreateTranslation(tubePos);

					SetGridShaderParameters(effect, world);
				}

				// Draw the mesh, using the effects set above.
				mesh.Draw();
			}
		}

        public Vector3 GetTubePosition()
        {
            return tubePos;
        }

        public ModelMesh GetTubeModelMesh()
        {
            return tubeModel.Meshes[0];
        }

		void SetGridShaderParameters(Effect effect, Matrix world)
		{
			effect.Parameters["world"].SetValue(world);
			effect.Parameters["view"].SetValue(cam.GetView());
			effect.Parameters["projection"].SetValue(cam.GetProjection());

            effect.Parameters["offsetXY"].SetValue(offsetXY);
			effect.Parameters["densityXY"].SetValue(densityXY);
			effect.Parameters["smoothnessXY"].SetValue(smoothnessXY);
			effect.Parameters["color1"].SetValue(color1);
			effect.Parameters["color2"].SetValue(color2);
		}

		void InitGridShaderParameters()
		{
			// the following values control the number of grid lines
			densityXY.X = 62.8f;
            offsetXY.X = 1.6f / densityXY.X;
            densityXY.Y = 40;
            offsetXY.Y = 1.6f / densityXY.Y;

			// the following values control thickness and smoothness of the grid lines
			smoothnessXY.X = 200;
			smoothnessXY.Y = 100;

			color1 = Color.Black.ToVector4();
		}

		void AdjustModelLocationRotation()
		{
			// adjust tube location and rotation of the model
			tubePos = new Vector3(0, 0, 0);
			tubeRotX = MathHelper.ToRadians(90);

            tubeLength = 20;

			// set cam location
			camPos = new Vector3(0, 0, -60);
		}

		// this function should contains per level data
		void SetLevelData()
		{
			AdjustModelLocationRotation();
			InitGridShaderParameters();
		}

		Model tubeModel;		
		Vector3 tubePos;
		float tubeRotX;
        float tubeLength;

		Vector3 camPos;
		Camera cam;
		SpriteBatch textRenderer;        
		Polyline poly;

#if DEBUG
        int polyVerticesCount;
        VertexPositionColor[] pointList = null;
#endif

        BasicEffect basicEffect = new BasicEffect(Sudum.Me.GraphicsDevice, null);
		Effect gridShaderEffect;
		public Vector2 densityXY = new Vector2(1, 1);
        public Vector2 offsetXY = new Vector2(0, 0);
		public Vector2 smoothnessXY = new Vector2(100, 100);
		public Vector4 color1 = new Vector4(0);
		public Vector4 color2 = new Vector4(0, 0.6f, 0, 1);

        StarsEffect stars = null;

        PlayerShip playerShip;
        BulletManager bulletManager;

        EnemyManager enemyManager;

        CollisionManager collisionManager;

		static GameLevel m_currentLevel;
	}
}
